import * as assert from 'assert';
import { createTimeline  } from '../../src/timeline';

describe('Timeline', () => {

  const timeline = createTimeline();
  timeline.takeSnapshot({ entities: { one: 10 } });

  // fake 100ms later
  timeline.takeSnapshot({ entities: { one: 5 } }, 100);

  // fake 200ms later
  timeline.takeSnapshot({ entities: { one: 0 } }, 200);

  describe('dealing with dropped snapshots', () => {

    const t = createTimeline(5);
    assert.equal(t.history.length, 0);

    t.takeSnapshot({ one: 10 });
    t.takeSnapshot({ one: 15 }, 20);
    t.takeSnapshot({ one: 18 }, 30);
    t.takeSnapshot({ one: 20 }, 40);

    assert.equal(t.history.length, 4);

    it('#takeSnapshot should drop old snapshots', () => {

      t.takeSnapshot({ one: 25 }, 50);
      assert.equal(t.history.length, 5);

      t.takeSnapshot({ one: 20 }, 60);
      assert.equal(t.history.length, 5);

      t.takeSnapshot({ one: 30 }, 70);
      assert.equal(t.history.length, 5);

      t.takeSnapshot({ one: 40 }, 80);
      assert.equal(t.history.length, 5);

    });

    it('should interpolate between removed state', () => {

      const t2 = createTimeline(5);
      t2.takeSnapshot({ one: 10 });
      t2.takeSnapshot({ one: 15 }, 20);
      t2.takeSnapshot({ one: 18 }, 30);
      t2.takeSnapshot({ one: 20 }, 40);
      t2.takeSnapshot({ one: 30 }, 50);
      t2.takeSnapshot({ one: 40 }, 60);
      assert.equal(t2.at(0).one, 15);

    });

  });

  describe('number of snapshots', () => {

    it('should not interpolate having only one snapshot', () => {

      const t = createTimeline();
      t.takeSnapshot({ entities: { one: 10 } });

      assert.equal(t.at(0).entities.one, 10);
      assert.equal(t.at(100).entities.one, 10);

    });

    it('should interpolate having only two snapshots', () => {

      const t = createTimeline();

      t.takeSnapshot({ entities: { one: 10 } });

      // fake 100ms later
      t.takeSnapshot({ entities: { one: 5 } }, 100);

      assert.equal(t.at(0).entities.one, 10);
      assert.equal(t.at(100).entities.one, 5);
      assert.equal(t.at(200).entities.one, 0);

    });

  });

  describe('#at (without interpolation/extrapolation)', () => {

    it('should find closest snapshot by time distance', () => {

      assert.equal(timeline.at(0, false).entities.one, 10);
      assert.equal(timeline.at(25, false).entities.one, 10);
      assert.equal(timeline.at(50, false).entities.one, 5);
      assert.equal(timeline.at(80, false).entities.one, 5);

      assert.equal(timeline.at(100, false).entities.one, 5);
      assert.equal(timeline.at(140, false).entities.one, 5);

      assert.equal(timeline.at(200, false).entities.one, 0);
      assert.equal(timeline.at(210, false).entities.one, 0);
      assert.equal(timeline.at(1000, false).entities.one, 0);

    });

  });

  describe('#at (with interpolation)', () => {

    it('no need to interpolate end of frames', () => {

      assert.equal(timeline.at(0).entities.one, 10);
      assert.equal(timeline.at(100).entities.one, 5);
      assert.equal(timeline.at(200).entities.one, 0);

    });

    it('should interpolate values between known snapshots', () => {

      assert.equal(timeline.at(50).entities.one, 7.5);
      assert.equal(timeline.at(150).entities.one, 2.5);

    });

    it('should extrapolate future time requests', () => {

      assert.equal(timeline.at(300).entities.one, -5);
      assert.equal(timeline.at(400).entities.one, -10);
      assert.equal(timeline.at(500).entities.one, -15);

    });

  });

});
